package com.example.gatewayzuul.filter;

import com.example.gatewayzuul.service.FeignPermissionService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Iterator;

@Component
public class PreRequestLogFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(
            PreRequestLogFilter.class);

    private static final String ALLOWED_STRINGS[] = {
            "stream",
            "authentication-permission",
            "check-permission"
    };

    @Autowired
    private FeignPermissionService feignPermissionService;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 该方法定义是否应该对请求进行过滤
     * @return true为应该，false为不应该
     */
    @Override
    public boolean shouldFilter() {
        boolean result = true;
        //获取当前请求上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //取出当前请求
        HttpServletRequest request = ctx.getRequest();
        //取出当前响应
        HttpServletResponse response = ctx.getResponse();

//        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        response.setHeader("Access-Control-Expose-Headers", "Location");

        //获取请求uri
        String requestURI = request.getRequestURI();

        if (requestURI != null && checkIsAllowed())
            result = false;

        return result;
    }
    
    /**
     * 该方法用于修减token，防止出现通配符注入的问题
     * @return true为通过，false为仍需进行权限认证
     */
    private String trimToken(String accessToken){
        accessToken = (accessToken == null) ? "null" : accessToken;
        // 替换掉所有通配符，以免发生注入
        accessToken = accessToken.replaceAll("[*?\\[\\]]","");
        if (accessToken.length() < 32 || "".equals(accessToken.trim())){
            accessToken = "null";
        }
        return accessToken;
    }

    /**
     * 该方法用于检查请求被允许直接通过过滤器或在有权限时直接通过过滤器
     * @return true为通过，false为仍需进行权限认证
     */
    private boolean checkIsAllowed(){
        Iterator<String> iterator = Arrays.stream(ALLOWED_STRINGS).iterator();
        //获取当前请求上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //取出当前请求
        HttpServletRequest request = ctx.getRequest();
        //如果请求方法为OPTIONS就直接通过
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())){
//            ctx.getResponse().setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        //如果请求中含有特定的允许通过的字符串，则也可以通过过滤器
        String requestURI = request.getRequestURI();
        while(iterator.hasNext()){
            String allowedString = iterator.next();
            if (requestURI.contains(allowedString)){
                return true;
            }
        }
        //从headers中取出key为accessToken值
        String accessToken = request.getHeader("accessToken");//这里我把token写入了headers
        if (accessToken != null){
            accessToken = trimToken(accessToken);
            return feignPermissionService.checkUserRequest(accessToken,requestURI);
        }
        //到最后都不能放行的话就只能拦截该请求
        return false;
    }

    @Override
    public Object run() {
        //获取当前请求上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //取出当前请求
        HttpServletRequest request = ctx.getRequest();
        logger.info("进入访问过滤器，访问的url:{}，访问的方法：{}", request.getRequestURL(), request.getMethod());

        // 判定请求是否能通过
        if (!checkIsAllowed()) {
            logger.info("该请求已被拒绝！");
            //使用Zuul来过滤这次请求
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            return null;
        }
        logger.info("该请求通过过滤器！");
        return null;
    }



}
